package com.snec.oauth2.oauth;


import com.snec.oauth2.oauth.properties.PearlOauth2Properties;
import com.snec.oauth2.oauth.exception.MyWebResponseExceptionTranslator;
import com.snec.oauth2.oauth.jwt.MyTokenEnhancer;
import com.snec.oauth2.oauth.sms.SmsCodeGranter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author 猪猪
 * @since 2022/6/20
 * 授权服务器的配置类
 */
@Configuration
@EnableConfigurationProperties(PearlOauth2Properties.class)
@EnableAuthorizationServer
public class MyAuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    // AuthorizationServer配置
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                // tokenkey这个endpoint当使用JwtToken且使用非对称加密时，资源服务用于获取公钥而开放的，这里指这个 endpoint完全公开
                .tokenKeyAccess("permitAll()")
                // checkToken这个endpoint完全公开
                .checkTokenAccess("permitAll()")
                //  允许表单认证
                .allowFormAuthenticationForClients();
    }

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Resource
    private TokenStore redisTokenStore;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Resource
    private MyWebResponseExceptionTranslator myWebResponseExceptionTranslator;

//    @Override
//    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//        // 配置客户端
//        clients
//                // 使用内存设置
//                .inMemory()
//                // client_id
//                .withClient("client")
//                // client_secret
//                .secret(passwordEncoder.encode("secret"))
//                // 授权类型: 授权码、刷新令牌、密码、客户端、简化模式、短信验证码 "refresh_token"
//                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "sms_code","wx")
//                // 授权范围，也可根据这个范围标识，进行鉴权
//                .scopes("admin:org","write:org","read:org")
//                .accessTokenValiditySeconds(300)
//                .refreshTokenValiditySeconds(3000)
//                // 授权码模式 授权页面是否自动授权
//                //.autoApprove(false)
//                // 拥有的权限
//                .authorities("add:user")
//                // 允许访问的资源服务 ID
//                //.resourceIds("oauth2-resource-server001-demo")
//                // 注册回调地址
//                .redirectUris("http://localhost:8160/code");
//    }


    @Resource
    private DataSource dataSource;
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //配置客户端存储到db 代替原来得内存模式
        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        clientDetailsService.setPasswordEncoder(passwordEncoder);
        System.out.println("经过默认客户端，拿取数据库数据"+passwordEncoder.encode("123456"));
        clients.withClientDetails(clientDetailsService);
    }

    // 端点配置
//    @Override
//    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        // 配置端点允许的请求方式
//        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
//        // 配置认证管理器
//        endpoints.authenticationManager(authenticationManager);
//        // 自定义异常翻译器，用于处理OAuth2Exception
//        endpoints.exceptionTranslator(myWebResponseExceptionTranslator);
//        // 重新组装令牌颁发者，加入自定义授权模式
//        endpoints.tokenGranter(getTokenGranter(endpoints));
///*      // 添加JWT令牌
//        // JWT令牌转换器
//        endpoints.accessTokenConverter(jwtAccessTokenConverter);
//        // JWT 存储令牌
//        endpoints.tokenStore(jwtTokenStore);*/
//    }


//    // 端点配置 存入redis中
//    @Override
//    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        // 配置端点允许的请求方式
//        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
//        // 配置认证管理器
//        endpoints.authenticationManager(authenticationManager);
//        // 自定义异常翻译器，用于处理OAuth2Exception
//        endpoints.exceptionTranslator(myWebResponseExceptionTranslator);
//        // 重新组装令牌颁发者，加入自定义授权模式
//        endpoints.tokenGranter(getTokenGranter(endpoints));
//      // 添加JWT令牌
//        // JWT令牌转换器
////        endpoints.accessTokenConverter(jwtAccessTokenConverter);
//        // Redis 存储令牌
//        endpoints.tokenStore(redisTokenStore);
//    }

    @Resource
    private UserDetailsService userDetailsService;

    @Resource
    private AuthorizationServerTokenServices myDefaultTokenServices;

    // 端点配置
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//      将增强的token设置到增强链中（形成jwt令牌）
//        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
//        enhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter));


        // 配置端点允许的请求方式
        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
        // 配置认证管理器
        endpoints.authenticationManager(authenticationManager);
        // 自定义异常翻译器，用于处理OAuth2Exception
        endpoints.exceptionTranslator(myWebResponseExceptionTranslator);
        // 重新组装令牌颁发者，加入自定义授权模式
        endpoints.tokenGranter(getTokenGranter(endpoints));
      // 添加JWT令牌
        // JWT令牌转换器
//        endpoints.accessTokenConverter(jwtAccessTokenConverter);
        // JWT 存储令牌
        endpoints.tokenStore(redisTokenStore);
        // 刷新令牌模式添加 userDetailsService
        endpoints.userDetailsService(userDetailsService);
        // 添加令牌增强器
        endpoints.tokenEnhancer(tokenEnhancer());
//        endpoints.tokenEnhancer(enhancerChain);
        // 修改默认端点路径
        endpoints.pathMapping("/oauth/token","/custom/token");
        // 添加自定义Token Services(自定义生成token)
//        endpoints.tokenServices(myDefaultTokenServices);
    }
    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new MyTokenEnhancer();
    }




    private TokenGranter getTokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
        // 默认tokenGranter集合
        List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));

        // 增加短信验证码模式
        granters.add(new SmsCodeGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));


        // 组合tokenGranter集合
        return new CompositeTokenGranter(granters);
    }


}
